<template>
  <div class="model-viewer-container">
    <div ref="container" class="main-view"></div>

    
    
    <div class="view-controls">
      <button @click="setView('front')">前视图</button>
      <button @click="setView('back')">后视图</button>
      <button @click="setView('left')">左视图</button>
      <button @click="setView('right')">右视图</button>
      <button @click="setView('top')">上视图</button>
      <button @click="setView('bottom')">下视图</button>
      <button @click="resetView">重置视图</button>
    </div>
    
    <div v-if="loading" class="loading-overlay">
      <div class="loading-content">
        <div class="spinner"></div>
        <div>加载中... {{ progress }}%</div>
      </div>
    </div>
    <div v-if="error" class="error-overlay">
      <div class="error-content">
        <div class="error-icon">⚠️</div>
        <div>加载失败: {{ error }}</div>
        <button @click="loadModel">重试</button>
      </div>
    </div>
  </div>
</template>

<script setup>
import { ref, watch, onMounted, onBeforeUnmount } from 'vue';
import * as THREE from 'three';
import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js';
import { OBJLoader } from 'three/addons/loaders/OBJLoader.js';
import { FBXLoader } from 'three/addons/loaders/FBXLoader.js';
import { STLLoader } from 'three/addons/loaders/STLLoader.js';
import { DRACOLoader } from 'three/addons/loaders/DRACOLoader.js';
import { ThreeMFLoader } from 'three/addons/loaders/3MFLoader.js';

const props = defineProps({
  modelUrl: {
    type: String,
    required: true
  },
  modelType: {
    type: String,
    required: true,
    validator: (value) => ['gltf', 'glb', 'obj', 'fbx', 'stl', '3mf'].includes(value)
  },
  backgroundColor: {
    type: String,
    default: '#f0f0f0'
  },
  showAxes: {
    type: Boolean,
    default: false
  },
  dracoDecoderPath: {
    type: String,
    default: 'https://www.gstatic.com/draco/versioned/decoders/1.5.6/'
  },
  modelColor: {
    type: String,
    default: '#aaaaaa'
  },
  targetSize: {
    type: Number,
    default: 3.0
  }
});

const emit = defineEmits(['loading', 'loaded', 'error', 'progress']);

const container = ref(null);
const loading = ref(false);
const progress = ref(0);
const error = ref(null);

let scene, camera, renderer, controls, currentModel, modelContainer;
let modelSize = new THREE.Vector3();

// 初始化场景
function initScene() {
  // 创建场景
  scene = new THREE.Scene();
  scene.background = new THREE.Color(props.backgroundColor);
  
  // 创建相机
  camera = new THREE.PerspectiveCamera(
    60,
    container.value.clientWidth / container.value.clientHeight,
    0.1,
    1000
  );
  camera.position.set(0, 0, 5);
  
  // 创建渲染器
  renderer = new THREE.WebGLRenderer({ 
    antialias: true,
    alpha: true
  });
  renderer.setSize(container.value.clientWidth, container.value.clientHeight);
  container.value.appendChild(renderer.domElement);
  
  // 添加轨道控制器
  controls = new OrbitControls(camera, renderer.domElement);
  controls.enableDamping = true;
  controls.dampingFactor = 0.05;
  
  // 添加光源
  addLights(scene);
  
  // 添加坐标轴辅助
  if (props.showAxes) {
    const axesHelper = new THREE.AxesHelper(5);
    scene.add(axesHelper);
  }

  // 创建模型容器
  modelContainer = new THREE.Group();
  scene.add(modelContainer);
}

// 添加光源
function addLights(scene) {
  const ambientLight = new THREE.AmbientLight(0xffffff, 0.8);
  scene.add(ambientLight);
  
  const directionalLight1 = new THREE.DirectionalLight(0xffffff, 1);
  directionalLight1.position.set(5, 10, 7);
  scene.add(directionalLight1);
  
  const directionalLight2 = new THREE.DirectionalLight(0xffffff, 0.5);
  directionalLight2.position.set(-5, -10, -7);
  scene.add(directionalLight2);
}

// 从URL加载模型
async function loadModel() {
  if (!props.modelUrl) return;
  
  loading.value = true;
  error.value = null;
  emit('loading', true);
  
  try {
    // 清除旧模型
    if (currentModel) {
      modelContainer.remove(currentModel);
      currentModel = null;
    }
    
    // 根据不同类型使用不同的加载方式
    switch (props.modelType) {
      case 'gltf':
      case 'glb':
        await loadGLTF(props.modelUrl);
        break;
      case 'obj':
        await loadOBJ(props.modelUrl);
        break;
      case 'fbx':
        await loadFBX(props.modelUrl);
        break;
      case 'stl':
        await loadSTL(props.modelUrl);
        break;
      case '3mf':
        await load3MF(props.modelUrl);
        break;
      default:
        throw new Error('不支持的模型格式');
    }
    
    emit('loaded');
  } catch (err) {
    error.value = err.message || '加载模型失败';
    emit('error', err);
  } finally {
    loading.value = false;
    emit('loading', false);
  }
}

// 加载GLTF/GLB模型
async function loadGLTF(url) {
  const loader = new GLTFLoader();
  
  const dracoLoader = new DRACOLoader();
  dracoLoader.setDecoderPath(props.dracoDecoderPath);
  loader.setDRACOLoader(dracoLoader);
  
  return new Promise((resolve, reject) => {
    loader.load(
      url,
      (gltf) => {
        currentModel = gltf.scene;
        prepareModel(currentModel);
        resolve();
      },
      (xhr) => {
        const percent = (xhr.loaded / xhr.total * 100).toFixed(2);
        progress.value = parseFloat(percent);
        emit('progress', progress.value);
      },
      (error) => {
        reject(error);
      }
    );
  });
}

// 加载OBJ模型
async function loadOBJ(url) {
  const loader = new OBJLoader();
  
  return new Promise((resolve, reject) => {
    loader.load(
      url,
      (object) => {
        currentModel = object;
        prepareModel(currentModel);
        resolve();
      },
      (xhr) => {
        const percent = (xhr.loaded / xhr.total * 100).toFixed(2);
        progress.value = parseFloat(percent);
        emit('progress', progress.value);
      },
      (error) => {
        reject(error);
      }
    );
  });
}

// 加载FBX模型
async function loadFBX(url) {
  const loader = new FBXLoader();
  
  return new Promise((resolve, reject) => {
    loader.load(
      url,
      (object) => {
        currentModel = object;
        prepareModel(currentModel);
        resolve();
      },
      (xhr) => {
        const percent = (xhr.loaded / xhr.total * 100).toFixed(2);
        progress.value = parseFloat(percent);
        emit('progress', progress.value);
      },
      (error) => {
        reject(error);
      }
    );
  });
}

// 加载STL模型
async function loadSTL(url) {
  const loader = new STLLoader();
  
  return new Promise((resolve, reject) => {
    loader.load(
      url,
      (geometry) => {
        const material = new THREE.MeshStandardMaterial({
          color: new THREE.Color(props.modelColor),
          metalness: 0.3,
          roughness: 0.7,
        });
        
        currentModel = new THREE.Mesh(geometry, material);
        prepareModel(currentModel);
        resolve();
      },
      (xhr) => {
        const percent = (xhr.loaded / xhr.total * 100).toFixed(2);
        progress.value = parseFloat(percent);
        emit('progress', progress.value);
      },
      (error) => {
        reject(error);
      }
    );
  });
}

// 加载3MF模型
async function load3MF(url) {
  const loader = new ThreeMFLoader();
  
  return new Promise((resolve, reject) => {
    loader.load(
      url,
      (object) => {
        currentModel = object;
        prepareModel(currentModel);
        resolve();
      },
      (xhr) => {
        const percent = (xhr.loaded / xhr.total * 100).toFixed(2);
        progress.value = parseFloat(percent);
        emit('progress', progress.value);
      },
      (error) => {
        reject(error);
      }
    );
  });
}

// 模型居中和缩放
function prepareModel(model) {
  // 计算模型的原始包围盒
  const box = new THREE.Box3().setFromObject(model);
  const center = new THREE.Vector3();
  box.getCenter(center);
  modelSize = box.getSize(new THREE.Vector3());
  
  // 创建容器组
  const container = new THREE.Group();
  container.add(model);
  
  // 调整模型位置使其几何中心与容器中心重合
  model.position.x -= center.x;
  model.position.y -= center.y;
  model.position.z -= center.z;
  
  // 计算缩放比例
  const maxDim = Math.max(modelSize.x, modelSize.y, modelSize.z);
  const scale = props.targetSize / maxDim;
  container.scale.set(scale, scale, scale);
  
  // 将容器添加到场景
  modelContainer.add(container);
  
  // 更新当前模型引用
  currentModel = container;
  
  // 调整相机位置
  const scaledSize = modelSize.clone().multiplyScalar(scale);
  const maxScaledDim = Math.max(scaledSize.x, scaledSize.y, scaledSize.z);
  camera.position.set(0, 0, maxScaledDim * 2.5);
  
  // controls.update();
  // 重置控制器
  resetView();
}

// 设置预设视图
function setView(view) {
  if (!currentModel) return;
  const scaledSize = modelSize.clone().multiplyScalar(currentModel.scale.x);
  const distance = Math.max(scaledSize.x, scaledSize.y, scaledSize.z)*2.5;
  // const distance = Math.max(modelSize.x, modelSize.y, modelSize.z) * 2;
  
  switch (view) {
    case 'front':
      camera.position.set(0, 0, distance);
      controls.target.set(0, 0, 0);
      break;
    case 'back':
      camera.position.set(0, 0, -distance);
      controls.target.set(0, 0, 0);
      break;
    case 'left':
      camera.position.set(-distance, 0, 0);
      controls.target.set(0, 0, 0);
      break;
    case 'right':
      camera.position.set(distance, 0, 0);
      controls.target.set(0, 0, 0);
      break;
    case 'top':
      camera.position.set(0, distance, 0);
      controls.up.set(0, 0, 1);
      controls.target.set(0, 0, 0);
      break;
    case 'bottom':
      camera.position.set(0, -distance, 0);
      controls.up.set(0, 0, -1);
      controls.target.set(0, 0, 0);
      break;
  }
  
  controls.update();
}

// 重置视图
function resetView() {
  if (!currentModel) return;
  
  const scaledSize = modelSize.clone().multiplyScalar(currentModel.scale.x);
  const maxScaledDim = Math.max(scaledSize.x, scaledSize.y, scaledSize.z);
  
  camera.position.set(0, 0, maxScaledDim * 2.5);
  controls.target.set(0, 0, 0);
  controls.update();
}

// 处理窗口大小变化
function onWindowResize() {
  camera.aspect = container.value.clientWidth / container.value.clientHeight;
  camera.updateProjectionMatrix();
  renderer.setSize(container.value.clientWidth, container.value.clientHeight);
}

// 动画循环
function animate() {
  requestAnimationFrame(animate);
  controls.update();
  renderer.render(scene, camera);
}

// 监听props变化
watch(() => [props.modelUrl, props.modelType, props.targetSize], () => {
  loadModel();
});

onMounted(() => {
  initScene();
  animate();
  window.addEventListener('resize', onWindowResize);
  loadModel();
});

onBeforeUnmount(() => {
  window.removeEventListener('resize', onWindowResize);
  if (renderer) {
    renderer.dispose();
  }
});
</script>

<style scoped>
.model-viewer-container {
  width: 100%;
  height: 100%;
  min-height: 600px;
  position: relative;
  display: flex;
  flex-direction: column;
}

.main-view {
  flex: 1;
  width: 100%;
  border: 1px solid #ddd;
  background-color: #f9f9f9;
}

.view-controls {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
  padding: 15px;
  background: #f0f0f0;
  border-top: 1px solid #ddd;
}

.view-controls button {
  padding: 8px 15px;
  background: #09f;
  color: white;
  border: none;
  border-radius: 4px;
  cursor: pointer;
  font-size: 14px;
  transition: all 0.2s;
}

.view-controls button:hover {
  background: #07d;
  transform: translateY(-1px);
}

.loading-overlay, .error-overlay {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  display: flex;
  justify-content: center;
  align-items: center;
  background-color: rgba(0, 0, 0, 0.5);
  z-index: 100;
}

.loading-content, .error-content {
  background: white;
  padding: 25px;
  border-radius: 10px;
  text-align: center;
  box-shadow: 0 0 20px rgba(0,0,0,0.2);
}

.spinner {
  border: 5px solid rgba(0, 0, 0, 0.1);
  width: 50px;
  height: 50px;
  border-radius: 50%;
  border-left-color: #09f;
  animation: spin 1s linear infinite;
  margin: 0 auto 15px;
}

.error-icon {
  font-size: 40px;
  margin-bottom: 15px;
}

@keyframes spin {
  0% { transform: rotate(0deg); }
  100% { transform: rotate(360deg); }
}

.error-overlay button {
  margin-top: 15px;
  padding: 8px 20px;
  background: #09f;
  color: white;
  border: none;
  border-radius: 5px;
  cursor: pointer;
  font-size: 16px;
  transition: background 0.3s;
}

.error-overlay button:hover {
  background: #07d;
}
</style>